﻿// -----------------------------------------------------------------------
// <copyright file="Helper.cs" company="none">
// Copyright (c) 2012 M. Alberti, xanatos(at)live.it
// Based on the articles found at 
// http://blogs.msdn.com/b/mitsu/archive/2010/03/02/c-4-expressions-blocks-part-i.aspx, 
// http://blogs.msdn.com/b/mitsu/archive/2010/03/02/c-4-expressions-variables-part-ii.aspx, and
// http://blogs.msdn.com/b/mitsu/archive/2010/03/02/c-4-expressions-loops-goto-label-if-and-even-for-part-iii.aspx.
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// </copyright>
// -----------------------------------------------------------------------

namespace FluentStatement
{
    using System;
    using System.Linq.Expressions;
    using System.Reflection;

    /// <summary>
    /// 
    /// </summary>
    public static class Helper
    {
        /// <summary>
        /// 
        /// </summary>
        private static readonly MethodInfo Visit = typeof(Visitor).GetMethod("Visit", BindingFlags.Public | BindingFlags.Static);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="visitor"></param>
        /// <returns></returns>
        public static Expression Expand(this Expression expression, Type visitor = null)
        {
            Visitor visitor2 = GetVisitor(visitor);

            Expression expression2 = visitor2.StartVisit(expression, expression.Type);
            return expression2;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TDelegate"></typeparam>
        /// <param name="expression"></param>
        /// <param name="visitor"></param>
        /// <returns></returns>
        public static Expression<TDelegate> Expand<TDelegate>(this Expression<TDelegate> expression, Type visitor = null)
        {
            if (!typeof(Delegate).IsAssignableFrom(typeof(TDelegate)))
            {
                throw new ArgumentException("TDelegate");
            }

            Type returnType = typeof(TDelegate).GetMethod("Invoke").ReturnType;

            Visitor visitor2 = GetVisitor(visitor);

            Expression expression2 = visitor2.StartVisit(expression.Body, returnType);
            Expression<TDelegate> expression3 = Expression.Lambda<TDelegate>(expression2, expression.Parameters);
            return expression3;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="visitor"></param>
        /// <returns></returns>
        private static Visitor GetVisitor(Type visitor)
        {
            if (visitor == null)
            {
                var visitor2 = new Visitor();
                return visitor2;
            }
            else if (!typeof(Visitor).IsAssignableFrom(visitor))
            {
                throw new ArgumentException("visitor");
            }
            else
            {
                ConstructorInfo constructor = visitor.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

                if (constructor == null)
                {
                    throw new ArgumentException("visitor");
                }

                var visitor2 = (Visitor)constructor.Invoke(new object[0]);
                return visitor2;
            }
        }
    }
}
